/* big_header.cpp */

#include "big_header.h"
#include <cstdlib>
#include <boost/cstdint.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>


#ifndef NO_NEED_TO_SWAP_ENDIAN
#include <dodo_utils/swap_endian.h>
#endif // NO_NEED_TO_SWAP_ENDIAN

namespace dodomet
{
namespace mm5
{
// Big_Header_Impl
Big_Header_Impl::Big_Header_Impl()
	: bhi_(bhi_data_, boost::extents[50][20], boost::fortran_storage_order()),
	bhr_(bhr_data_, boost::extents[20][20], boost::fortran_storage_order()),
	bhic_(bhic_data_, boost::extents[50][20], boost::fortran_storage_order()),
	bhrc_(bhrc_data_, boost::extents[20][20], boost::fortran_storage_order()),
	use_count_(1)
{
	bhi_.reindex(1);
	bhr_.reindex(1);
	bhic_.reindex(1);
	bhrc_.reindex(1);
}

Big_Header_Impl::Big_Header_Impl(const Big_Header_Impl& orig)
	: bhi_(bhi_data_, boost::extents[50][20], boost::fortran_storage_order()),
	bhr_(bhr_data_, boost::extents[20][20], boost::fortran_storage_order()),
	bhic_(bhic_data_, boost::extents[50][20], boost::fortran_storage_order()),
	bhrc_(bhrc_data_, boost::extents[20][20], boost::fortran_storage_order()),
	use_count_(1)
{
	std::memcpy(bhi_data_, orig.bhi_data_, sizeof(fortran_int) * 50 * 20);
	std::memcpy(bhr_data_, orig.bhr_data_, sizeof(fortran_int) * 20 * 20);
	for(int i = 0; i < 20 * 50; ++i)
	{
		bhic_data_[i] = orig.bhic_data_[i];
	}
	for(int i = 0; i < 20 * 20; ++i)
	{
		bhrc_data_[i] = orig.bhrc_data_[i];
	}
}

bool Big_Header_Impl::is_only()
{
	return use_count_ == 1;
}
// reading from stream
void Big_Header_Impl::read(std::fstream& stream)
{
	fortran_int dum;
	stream.read(reinterpret_cast<char*>(&dum), sizeof(dum));

	stream.read(reinterpret_cast<char*>(bhi_data_), sizeof(fortran_int) * 20 * 50);
	stream.read(reinterpret_cast<char*>(bhr_data_), sizeof(fortran_real) * 20 * 20);

#ifndef NO_NEED_TO_SWAP_ENDIAN
	for(fortran_int* p = bhi_data_; p != bhi_data_ + 20 * 50; ++p)
	{
		dodomet::swap_endian(*p);
	}
	for(fortran_real* p = bhr_data_; p != bhr_data_ + 20 * 20; ++p)
	{
		dodomet::swap_endian(*p);
	}
#endif //NO_NEED_TO_SWAP_ENDIAN
	char raw_chars[81];
	for(int j = 1; j <= 20; ++j)
	{
		for(int i = 1; i <= 50; ++i)
		{
			stream.read(raw_chars, 80);
			raw_chars[80] = '\0';
			bhic_[i][j] = std::string(raw_chars);
		}
	}
	for(int j = 1; j <= 20; ++j)
	{
		for(int i = 1; i <= 20; ++i)
		{
			stream.read(raw_chars, 80);
			raw_chars[80] = '\0';
			bhrc_[i][j] = std::string(raw_chars);
		}
	}

	stream.read(reinterpret_cast<char*>(&dum), sizeof(dum));
}


boost::posix_time::ptime Big_Header_Impl::start_time(int program_index) const
{
	if(program_index == 0)
		program_index = data_type();

	if(program_index == data_types::TERRAIN)
		return boost::date_time::not_a_date_time;
	else if(bhi(5, program_index) == INVALID_VALUE)
		return boost::date_time::not_a_date_time;
	else
		return boost::posix_time::ptime(
			boost::gregorian::date(
				bhi(5, program_index),
				bhi(6, program_index),
				bhi(7, program_index)
				),
			boost::posix_time::time_duration(
				bhi(8, program_index),
				bhi(9, program_index),
				bhi(10, program_index),
				bhi(11, program_index) * 100
				)
			);
}

boost::posix_time::time_duration Big_Header_Impl::time_interval(int program_index) const
{
	if(program_index == 0)
		program_index = data_type();
	
	if(program_index == data_types::TERRAIN)
		return boost::date_time::pos_infin;
	else if(bhr(1, program_index) < 0)
		return boost::date_time::pos_infin;
	else
		return boost::posix_time::microseconds(
			bhr(1, program_index) * 1.0E6
			);
}

int Big_Header_Impl::mm5_version(int program_index) const
{
	if(program_index == 0)
		program_index = data_type();

	return bhi(2, program_index);
}

int Big_Header_Impl::program_version(int program_index) const
{
	if(program_index == 0)
		program_index = data_type();

	return bhi(3, program_index);
}

int Big_Header_Impl::program_minor_version(int program_index) const
{
	if(program_index == 0)
		program_index = data_type();

	return bhi(4, program_index);
}
	
int Big_Header_Impl::num_vertical_level(int program_index) const
{
	if(program_index == 0)
		program_index = data_type();
	
	if(program_index == data_types::TERRAIN)
		return 1;
	else
		return bhi(12, program_index);
}

int Big_Header_Impl::data_type() const
{
	return bhi(1, 1);
}

// Big_Header
Big_Header::Big_Header()
	: impl_(new Big_Header_Impl())
{
}

Big_Header::Big_Header(const Big_Header& orig)
	: impl_(orig.impl_)
{
	++impl_->use_count_;
}

Big_Header::~Big_Header()
{
	if(--impl_->use_count_ == 0)
		delete impl_;
}

Big_Header& Big_Header::operator= (const Big_Header& orig)
{
	++orig.impl_->use_count_;
	if(--impl_->use_count_ == 0)
		delete impl_;
	impl_ = orig.impl_;
}

void Big_Header::on_write()
{
	if(impl_->is_only())
		return;
	Big_Header_Impl *tmp = impl_;
	impl_ = new Big_Header_Impl(*tmp);
	--tmp->use_count_;
}

void Big_Header::read(std::fstream& stream)
{
	on_write();
	impl_->read(stream);
}

boost::posix_time::ptime Big_Header::start_time(int program_index) const
{
	return impl_->start_time(program_index);
}

boost::posix_time::time_duration Big_Header::time_interval(int program_index) const
{
	return impl_->time_interval(program_index);
}

int Big_Header::mm5_version(int program_index) const
{
	return impl_->mm5_version(program_index);
}

int Big_Header::program_version(int program_index) const
{
	return impl_->program_version(program_index);
}

int Big_Header::program_minor_version(int program_index) const
{
	return impl_->program_minor_version(program_index);
}
	
int Big_Header::num_vertical_level(int program_index) const
{
	return impl_->num_vertical_level(program_index);
}

int Big_Header::data_type() const
{
	return impl_->data_type();
}

} // namespace mm5
} // namespace dodomet
